﻿using System.Reflection;
using System.Text;
using rpc_service.cache;
using rpc_service.config;
using rpc_service.core.support.assembly.roslyn.handle;
using rpc_service.core.interceptor;
using rpc_service.core.registry;
using rpc_service.util;
using Microsoft.Extensions.Logging;

namespace rpc_service.core.support.assembly.roslyn;

public abstract class RoslynMethodBaseStep : RoslynStep
{
    protected RoslynParamHandle roslynParamHandle = new RoslynParamHandle();
    protected RoslynTypeHandle roslynTypeHandle = new RoslynTypeHandle();
    protected RoslynUrlHandle roslynUrlHandle;
    private readonly List<Type> _interceptors;
    private readonly Type _pollType;

    public RoslynMethodBaseStep(List<Type> interceptors, Type pollType, Type registryType)
    {
        _interceptors = interceptors;
        _pollType = pollType;
        roslynUrlHandle = new RoslynUrlHandle(_pollType, registryType);
    }
    
    public StringBuilder process(StringBuilder stringBuilder, Type targetType, MethodInfo targetMethod)
    {
        string httpMethod = getMethod(targetMethod);
        
        if (!isPass(httpMethod, targetMethod.ReturnType))
        {
            return stringBuilder;
        }

        return execute(stringBuilder, targetType, targetMethod);
    }
    
    protected void generateMethod(StringBuilder stringBuilder, MethodInfo targetMethod,
        Action<StringBuilder, bool> body)
    {
        StringBuilder typeName = roslynParamHandle.GenerateReturnType(targetMethod);
        bool isTask = targetMethod.ReturnType == "Task".GetTypeByStr() ||
                      (targetMethod.ReturnType.IsGenericType &&
                       targetMethod.ReturnType.GetGenericTypeDefinition() == "Task<>".GetTypeByStr());
        string task = isTask ? "async" : "";
        StringBuilder paramStringBuilder = roslynParamHandle.GenerateParam(targetMethod);

        stringBuilder.AppendLine($@"public {task} {typeName} {targetMethod.Name}({paramStringBuilder})");

        stringBuilder.AppendLine("{");
        body(stringBuilder, isTask);
        stringBuilder.AppendLine("}");
    }

    protected void generateHttpClient(StringBuilder stringBuilder, Type targetType, MethodInfo targetMethod, bool isTask, Action<StringBuilder, bool> body)
    {
        Type httpClientType = "HttpClient".GetTypeByStr();
        stringBuilder.AppendLine($@"using ({httpClientType} client = new {httpClientType}())");
        stringBuilder.AppendLine("{");

        stringBuilder.AppendLine($@"");
        generateHeader(stringBuilder, targetMethod, body);
        stringBuilder.AppendLine($@"    client.Timeout = TimeSpan.FromSeconds({LoadConfig.Load().WaitDuration});");
        
        // 生成拼接参数
        roslynUrlHandle.GetUrl(stringBuilder, targetType, targetMethod);
        roslynParamHandle.GenerateUrlParams(stringBuilder, targetMethod);
        
        string iDictionary = roslynTypeHandle.Handle("IDictionary<string, object>".GetTypeByStr());
        string dictionary = roslynTypeHandle.Handle("Dictionary<string, object>".GetTypeByStr());
        stringBuilder.AppendLine($@"{iDictionary} parameters = new {dictionary}();");
        stringBuilder.AppendLine("parameters[\"client\"] = client;");
        stringBuilder.AppendLine("parameters[\"url\"] = urlBuilder.ToString();");
        stringBuilder.AppendLine("parameters[\"data\"] = json;");
        
        // 增加拦截器
        if (!targetMethod.GetCustomAttribute<RPCMethod>()!.NoInterceptorNeed)
        {
            foreach (Type interceptor in _interceptors)
            {
                stringBuilder.AppendLine(
                    $@"{interceptor} {StringUtil.LowerFirstCharAsync(interceptor.ToString().Split(".").Last())} = new {interceptor}();");
                stringBuilder.AppendLine(
                    $@"{StringUtil.LowerFirstCharAsync(interceptor.ToString().Split(".").Last())}.PreInterceptor(parameters);");
            }
        }
        
        body(stringBuilder, isTask);
        stringBuilder.AppendLine("}");
    }

    protected void generateHeader(StringBuilder stringBuilder, MethodInfo targetMethod, Action<StringBuilder, bool> body)
    {
        List<RPCHeader> rpcHeaders = targetMethod.GetCustomAttributes<RPCHeader>().ToList();
        foreach (RPCHeader rpcHeader in rpcHeaders)
        {
            if (rpcHeader.IsDelegate)
            {
                stringBuilder.AppendLine($@"client.DefaultRequestHeaders.Add(""{rpcHeader.Key}"", MethodUtil.InvokeMethod(""{rpcHeader.Value}""));");
            }
            else
            {
                stringBuilder.AppendLine($@"client.DefaultRequestHeaders.Add(""{rpcHeader.Key}"", ""{rpcHeader.Value}"");");
            }
        }
    }

    protected void generateRetryCount(StringBuilder stringBuilder, bool isTask, Action<StringBuilder, bool> body)
    {
        Type intType = "Int32".GetTypeByStr();
        stringBuilder.AppendLine($@"for ({intType} retryCount = 0; retryCount < {LoadConfig.Load().RetryCount}; retryCount++)");
        stringBuilder.AppendLine("{");
        body(stringBuilder, isTask);
        stringBuilder.AppendLine("}");
    }
    
    protected void generateTryCatch(StringBuilder stringBuilder, bool isTask, Action<StringBuilder, bool> body)
    {
        stringBuilder.AppendLine("try");
        stringBuilder.AppendLine("{");
        body(stringBuilder, isTask);
        stringBuilder.AppendLine("}");
        stringBuilder.AppendLine("catch (HttpRequestException e)");
        stringBuilder.AppendLine("{");
        generateLogger(stringBuilder, "$\"Request error: {e.Message}\"", 1);
        stringBuilder.AppendLine("    throw new HttpRequestException(e.Message);");
        stringBuilder.AppendLine("}");
        stringBuilder.AppendLine("catch (Exception e)");
        stringBuilder.AppendLine("{");
        generateLogger(stringBuilder, "$\"Request error: {e.Message}\"", 1);
        stringBuilder.AppendLine("    throw new Exception(e.Message);");
        stringBuilder.AppendLine("}");
    }

    protected void generateReturn(StringBuilder stringBuilder, Type targetType, MethodInfo targetMethod, bool isTask)
    { 
        // 增加拦截器
        if (!targetMethod.GetCustomAttribute<RPCMethod>()!.NoInterceptorNeed)
        {
            foreach (Type interceptor in _interceptors)
            {
                stringBuilder.AppendLine(
                    $@"{StringUtil.LowerFirstCharAsync(interceptor.ToString().Split(".").Last())}.PostInterceptor(parameters);");
            }
        }

        stringBuilder.Append(roslynParamHandle.GenerateReturn(targetMethod));
    }
    
    private string getMethod(MethodInfo targetMethod)
    {
        return targetMethod.GetCustomAttribute<RPCMethod>()!.Method;
    }
    
    private void generateLogger(StringBuilder stringBuilder, string message, int type)
    {
        switch (type)
        {
            case 0:
                stringBuilder.AppendLine($@"_logger.LogInformation({message});");
                break;
            case 1:
                stringBuilder.AppendLine($@"_logger.LogError({message});");
                break;
        }
    }
    
    protected abstract bool isPass(string httpMethod, Type returnType);
    
    protected abstract StringBuilder execute(StringBuilder stringBuilder, Type targeType, MethodInfo targetMethod);
}